/*!	 value.cpp
**	 Template Header
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2007, 2008 Chris Moore
**  Copyright (c) 2011 Carlos López
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "value.h"
#include "general.h"
#include <synfig/localization.h>
#include <ETL/stringf>
#include "canvas.h"
#include "valuenodes/valuenode_bone.h"
#include "gradient.h"
#include "bone.h"
#include "matrix.h"
#include "boneweightpair.h"
#include "transformation.h"

#include "vector.h"
#include "time.h"
#include "segment.h"
#include "color.h"

#endif

using namespace synfig;
using namespace std;
using namespace etl;

#define TRY_FIX_FOR_BUG_27

ValueBase::ValueBase():
    type(&type_nil), data(0), ref_count(0), loop_(0), static_(0), interpolation_(INTERPOLATION_UNDEFINED)
{
#ifdef INITIALIZE_TYPE_BEFORE_USE
    type->initialize();
#endif
}

ValueBase::ValueBase(Type &x):
    type(&type_nil), data(0), ref_count(0), loop_(0), static_(0), interpolation_(INTERPOLATION_UNDEFINED)
{
#ifdef INITIALIZE_TYPE_BEFORE_USE
    type->initialize();
#endif
    create(x);
}

ValueBase::~ValueBase()
{
    clear();
}

#ifdef _DEBUG
String
ValueBase::get_string() const
{
    Operation::ToStringFunc func =
        Type::get_operation<Operation::ToStringFunc>(
            Operation::Description::get_to_string(type->identifier));
    return func == NULL ? "Invalid type" : func(data);
}
#endif	// _DEBUG

bool
ValueBase::is_valid()const
{
    return type != &type_nil && ref_count;
}

void
ValueBase::create(Type &type)
{
#ifdef INITIALIZE_TYPE_BEFORE_USE
    type.initialize();
#endif

    if (type == type_nil) {
        clear();
        return;
    }

    Operation::CreateFunc func =
        Type::get_operation<Operation::CreateFunc>(
            Operation::Description::get_create(type.identifier));
    assert(func != NULL);
    clear();
    this->type = &type;
    data = func();
    ref_count.reset();
}

void
ValueBase::copy(const ValueBase& x)
{
    Operation::CopyFunc func =
        Type::get_operation<Operation::CopyFunc>(
            Operation::Description::get_copy(type->identifier, x.type->identifier));

    if (func != NULL) {
        if (!ref_count.unique()) {
            create();
        }

        func(data, x.data);
    } else {
        Operation::CopyFunc func =
            Type::get_operation<Operation::CopyFunc>(
                Operation::Description::get_copy(x.type->identifier, x.type->identifier));

        if (func != NULL) {
            if (!ref_count.unique()) {
                create(*x.type);
            }

            func(data, x.data);
        }
    }

    copy_properties_of(x);
}

void
ValueBase::copy_properties_of(const ValueBase& x)
{
    loop_ = x.loop_;
    static_ = x.static_;
    interpolation_ = x.interpolation_;
}

bool
ValueBase::empty()const
{
    return !is_valid() || (type == &type_list ? get_list().empty() : false);
}

Type&
ValueBase::get_contained_type()const
{
    if (type != &type_list || empty()) {
        return type_nil;
    }

    return get_list().front().get_type();
}

ValueBase&
ValueBase::operator=(const ValueBase& x)
{
    if (data != x.data) {
        Type &current_type = *type;
        Type &new_type = *x.type;
        Operation::CopyFunc func =
            Type::get_operation<Operation::CopyFunc>(
                Operation::Description::get_copy(current_type.identifier, new_type.identifier));

        if (func != NULL) {
            create(current_type);
            func(data, x.data);
        } else {
            clear();
            type = x.type;
            data = x.data;
            ref_count = x.ref_count;
        }
    }

    loop_ = x.loop_;
    static_ = x.static_;
    interpolation_ = x.interpolation_;
    return *this;
}

void
ValueBase::clear()
{
    if (ref_count.unique() && data) {
        Operation::DestroyFunc func =
            Type::get_operation<Operation::DestroyFunc>(
                Operation::Description::get_destroy(type->identifier));
        assert(func != NULL);
        func(data);
    }

    ref_count.detach();
    data = 0;
    type = &type_nil;
}

Type&
ValueBase::ident_type(const String &str)
{
    Type *type = Type::try_get_type_by_name(str);
    return type == NULL ? type_nil : *type;
}

bool
ValueBase::operator==(const ValueBase& rhs)const
{
    Operation::EqualFunc func =
        Type::get_operation<Operation::EqualFunc>(
            Operation::Description::get_equal(type->identifier, rhs.type->identifier));
    return func == NULL ? false : func(data, rhs.data);
}

bool
ValueBase::operator<(const ValueBase& rhs)const
{
    Operation::LessFunc func =
        Type::get_operation<Operation::LessFunc>(
            Operation::Description::get_less(type->identifier, rhs.type->identifier));
    return func == NULL ? false : func(data, rhs.data);
}